# @Author: huguangzhi
# @design: E480
# @ContactEmail : huguangzhi@ucsdigital.com.com
# @ContactPhone : 13121961510
# @Desc  :

import numpy as np

data = np.loadtxt('ua.base',delimiter='\t',dtype=int)

rows, row_pos = np.unique(data[:,0], return_inverse=True)
cols, col_pos = np.unique(data[:,1], return_inverse=True)

R = np.zeros((len(rows), len(cols)),dtype=int)
R[row_pos,col_pos] = data[:,2]

L = []


def LFM_grad_desc(R, K, max_iter=10000, alpha=0.0001, lamda=0.001):
    # 获取数据维度
    M = len(R)  # 拿到用户数
    N = len(R[0])  # 拿到物品总数

    # 初始化P和Q的值，生成随机的
    P = np.random.rand(M, K)
    Q = np.random.rand(K, N)

    #     进入迭代
    for step in range(max_iter):
        cost = 0
        for u in range(M):
            for i in range(N):
                #                 过滤拿到评分大于0的评价，根据评价大于0的来进行迭代计算
                if R[u][i] > 0:
                    ui = np.dot(P[u, :], Q[:, i]) - R[u][i]  # 计算当前迭代次数中 P Q矩阵对应的损失梯度

                for k in range(K):
                    P[u][k] = P[u][k] - alpha * (2 * ui * Q[k][i] + 2 * lamda * P[u][k])
                    Q[k][i] = Q[k][i] - alpha * (2 * ui * P[u][k] + 2 * lamda * Q[k][i])

        # 计算当前损失函数
        cost = 0
        for u in range(M):
            for i in range(N):
                if R[u][i] > 0:
                    cost += (np.dot(P[u, :], Q[:, i]) - R[u][i]) ** 2
                    # 加上正则化项
                    for k in range(K):
                        cost += lamda * (P[u][k] ** 2 + Q[k][i] ** 2)
        if cost < 0.0001:
            break

    return P, Q.T

K = 10 # 超参，定义矩阵的维度
alpha = 0.0002 # 定义梯度的步长
lamda = 0.004 # 定义正则项的系数
iter_max = 5000 #定义最大迭代次数

P, Q = LFM_grad_desc(R, K, iter_max, alpha, lamda)
